package rtk;

import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import rtk.olinkstar.RtkService;


/**
 * Created by apple on 2017/5/28.
 */

public class RtkManager {

    private static WeakReference<Context> mContextWeakReference;
    private static final RtkManager mInstance = new RtkManager();
    private final BleManager mBleManager = new BleManager();
    private final List<Object> mListeners = new ArrayList<>();

    private final RtkHub mRtkHub = new RtkHub(mBleManager, new RtkService.OnNmeaListener() {
        @Override
        public void onNmea(String nmea) {
            if(nmea==null)
                return;
            if ( nmea.startsWith("$GPGGA")||  nmea.startsWith("$GNGGA")) {
                if (mRtkThread != null) {
                    mRtkThread.updateGGA(nmea);
                }
            }
        }
    });

    private RtkThread mRtkThread;
    private Timer mSchedule;


    private String mHost;
    private Integer mPort;
    private String mMountPoint;
    private String mAccount;
    private String mPassword;


    private boolean isRtcmConnected;

    public void init(@NonNull Context context) {
        mContextWeakReference = new WeakReference<>(context.getApplicationContext());
    }

    public static Context getContext() {
        return mContextWeakReference != null ? mContextWeakReference.get() : null;
    }

    public static RtkManager getInstance() {
        return mInstance;
    }

    public void connect(BluetoothDevice device) {
        mBleManager.connect(device, mGattCallback);
    }

    public void disconnect() {
        stopSchedule();
        fireBle(false);
        stopRtkServer();
        if (mBleManager != null)
            mBleManager.disconnect();
        if (mRtkHub != null)
            mRtkHub.stop();
    }


    public BleManager getBleManager() {
        return mBleManager;
    }

    public void addRtkListener(RtkListener listener) {
        if (listener instanceof FragmentActivity || listener instanceof Activity || listener instanceof Fragment) {
            addRtkListener(listener, true);
        } else {
            addRtkListener(listener, false);
        }
    }

    public synchronized void addRtkListener(RtkListener listener, boolean weak) {
        synchronized (mListeners) {
            removeListener(listener);
            if (weak) {
                mListeners.add(new WeakReference(listener));
            } else
                mListeners.add(listener);
        }
    }

    public synchronized void removeListener(RtkListener listener) {
        synchronized (mListeners) {
            List<Object> list = new ArrayList<>();
            for (int i = 0; i < mListeners.size(); i++) {
                Object obj = mListeners.get(i);
                if (obj instanceof RtkListener && obj.equals(listener)) {
                    list.add(obj);
                } else if (obj instanceof WeakReference) {
                    RtkListener rtkListener = ((WeakReference<RtkListener>) obj).get();
                    if (rtkListener == null || rtkListener.equals(listener))
                        list.add(obj);
                }
            }
            mListeners.removeAll(list);
        }
    }

    public synchronized void setRtkService(String host, Integer port, String accessPoint, String account, String password) {
        this.mHost = host;
        this.mPort = port;
        this.mMountPoint = accessPoint;
        this.mAccount = account;
        this.mPassword = password;
        if (mRtkThread != null)
            mRtkThread.setParams(mHost, mPort, mMountPoint, mAccount, mPassword);
    }


    public synchronized void startRtkServer(long interval) {
        stopRtkServer();
        mRtkThread = new RtkThread();
        mRtkThread.setParams(mHost, mPort, mMountPoint, mAccount, mPassword);
        mRtkThread.setRtkDataListener(new RtkThread.RtkDataListener() {
            @Override
            public void onRtkData(byte[] data) {
                if (mRtkHub != null) {
                    mRtkHub.setRtcmThread(true);
                    mRtkHub.SdkData(data, true);
                }
            }

            @Override
            public void onRtkServerState(boolean conn) {
                isRtcmConnected = conn;
            }
        });
        mRtkThread.setInterval(interval);
        mRtkThread.start();
    }

    public synchronized void stopRtkServer() {
        if (mRtkThread != null) {
            mRtkThread.setRtkDataListener(null);
            mRtkThread.cancel();
            mRtkThread = null;
        }
        if (mRtkHub != null) {
            mRtkHub.setRtcmThread(false);
        }
    }


    public synchronized void startSchedule(long interval) {
        stopSchedule();
        startRtkServer(interval);
        mSchedule = new Timer();
        mSchedule.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    if (mRtkHub == null)
                        return;
                    RtkData data = getRtkData();
                    if (data != null)
                        fireRtkListener(new onRtkListener() {
                            @Override
                            public void onRtkListener(RtkListener listener) {
                                listener.onRtkDataSchedule(data);
                            }
                        });
                } catch (Exception e) {
                }
            }
        }, 0, interval);
    }

    public RtkData getRtkData() {
        RtkData data = new RtkData();
        RtkHub hub = mRtkHub;
        if (hub != null) {
            try {
                data.setAccuracy(hub.getAccuracy());
                data.setLongitude(hub.getLongitude());
                data.setLatitude(hub.getLatitude());
                data.setAltitude(hub.getAltitude());
                data.setMode(hub.getMode());
                data.setSpeed(hub.getSpeed());
                data.setBearing(hub.getBearing());
                data.setuStates(hub.getUStates());
                data.setUtcDate(hub.getUtcDateTime());
                data.setBleRssi(hub.getBleRssi());
                data.setDevicePower(hub.getDevicePower());
            } catch (Exception e) {

            }
        }
        return data;
    }


    public synchronized void stopSchedule() {
        if (mSchedule != null)
            mSchedule.cancel();
        mSchedule = null;
        stopRtkServer();
    }

    public boolean isBleConnected() {
        return mBleManager.isConnected();
    }

    public boolean isRtcmConnected() {
        return isRtcmConnected;
    }

    public interface RtkListener {

        void onBleConnectionState(Boolean connected);

        void onRtkDataSchedule(RtkData data);
    }

    public interface onRtkListener {
        void onRtkListener(RtkListener listener);
    }

    private void fireRtkListener(onRtkListener onRtkListener) {
        synchronized (mListeners) {
            for (Object obj : mListeners) {
                try {
                    if (obj instanceof RtkListener) {
                        onRtkListener.onRtkListener((RtkListener) obj);
                    } else if (obj instanceof WeakReference) {
                        RtkListener rtkListener = ((WeakReference<RtkListener>) obj).get();
                        if (rtkListener != null)
                            onRtkListener.onRtkListener(rtkListener);
                    }
                } catch (Exception e) {

                }
            }
        }
    }

    public final GattCallback mGattCallback = new GattCallback() {
        @Override
        public void onDisconnected() {
            fireBle(false);
            if (mRtkHub != null)
                mRtkHub.onDisconnected();
        }

        @Override
        public void onConnectionError() {
            fireBle(false);
            if (mRtkHub != null)
                mRtkHub.onConnectionError();
        }

        @Override
        public void onTimeOut() {
            fireBle(false);
            if (mRtkHub != null)
                mRtkHub.onTimeOut();
        }

        @Override
        public void onConnected(BluetoothDevice device) {
            fireBle(true);
            if (mRtkHub != null)
                mRtkHub.onConnected(device);
        }

        @Override
        public void onRead(byte[] data) {
            if (mRtkHub != null)
                mRtkHub.onRead(data);
        }

        @Override
        public void onWrite(boolean b) {
            if (mRtkHub != null)
                mRtkHub.onWrite(b);
        }

        @Override
        public void onRSSI(int rssi, int status) {
            if (mRtkHub != null)
                mRtkHub.onRSSI(rssi, status);
        }

    };

    public void fireBle(boolean b) {
        fireRtkListener(new onRtkListener() {
            @Override
            public void onRtkListener(RtkListener listener) {
                listener.onBleConnectionState(b);
            }
        });
    }

}
